// ScsiCommand.cpp : implementation file
//

#include "stdafx.h"
#include "Globals.h"
#include "device.h"
#include <dbt.h>						// For DeviceChange.
#include <winioctl.h>					// For DeviceIOCtl.
#include "ScsiCommand.h"


#ifdef _DEBUG
//#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define FILE_DEVICE_MASS_STORAGE 0x02d

/////////////////////////////////////////////////////////////////////////////
// CScsiCommand
CScsiCommand::CScsiCommand()
{ 
	fd = INVALID_HANDLE_VALUE; 
	CurrentDevice = NULL;
	MCNHandle = 0;
}

CScsiCommand::~CScsiCommand()
{ 
	if (fd != INVALID_HANDLE_VALUE) 
		CloseAssociateHandle(); 
}
/////////////////////////////////////////////////////////////////////////////
// CScsiCommand message handlers

int CScsiCommand::associate (char *file)
{ 
	try
	{
		if (fd != INVALID_HANDLE_VALUE) 
			return fd != INVALID_HANDLE_VALUE;
		sprintf_s(dev, 100, "\\\\.\\%.*s",sizeof(dev)-5, file);
		fd=CreateFile (dev, GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,0,NULL);
		SAFE_LOG(out, "associate -  handle created", true);
		return fd != INVALID_HANDLE_VALUE;
	}
	catch (CFileException* e)
	{
		SAFE_LOG(out, "CScsiCommand::associate - ", false);
		SAFE_LOG(out, e, true);
		fd=INVALID_HANDLE_VALUE;
		return 1;
	}
}

void CScsiCommand::CloseAssociateHandle()
{
	try
	{
		if (fd!=INVALID_HANDLE_VALUE) 
		{
			CloseHandle (fd);		//close any previous opens
			fd=INVALID_HANDLE_VALUE;
			DisableMCN(FALSE); 
		}
		SAFE_LOG(out, "CloseAssociateHandle -  handle closed", true);
	}
	catch (CFileException* e)
	{
		SAFE_LOG(out, "CScsiCommand::CloseAssociateHandle - ", false);
		SAFE_LOG(out, e, true);
		fd=INVALID_HANDLE_VALUE;
		return;
	}
}

unsigned char& CScsiCommand::sense(size_t i)  
{ 
	return p.sense[i]; 
}

unsigned char* CScsiCommand::sense()	    
{ 
	return p.sense;    
}

void CScsiCommand::timeout(int i)		    
{ 
	p.spt.TimeOutValue=i; 
}

int CScsiCommand::DisableMCN(BOOL state)
{
	int status = -1;
	if (state)
	{
		SAFE_LOG(out, "DisableMCN(TRUE)", true);	
		MCNHandle = CreateFile(dev, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, NULL);	
		if (MCNHandle == INVALID_HANDLE_VALUE)
		{
			SAFE_LOG(out, "DisableMCN - Unexpected result from CreateFile, failed to change MCN status", true);
			return status;
		}
	}
	else
	{
		if (MCNHandle == 0)
			return 0;
		SAFE_LOG(out, "DisableMCN(FALSE)", true);
	}
	BytesReturned = 0;
	status = DeviceIoControl(MCNHandle, IOCTL_STORAGE_MCN_CONTROL, &state, sizeof(BOOL), NULL, 0, &BytesReturned, NULL);
	if(!status)
	{
		//Failed result
		CString tmpmsg;
		DoGetLastError(&tmpmsg);
		SAFE_LOG(out, "GetLastError() returned - ", false);			
		SAFE_LOG(out, tmpmsg.GetBuffer(0), false);	 //GetLastError includes a CR
		tmpmsg.ReleaseBuffer();
		if(state)
		{
			SAFE_LOG(out, "DisableMCN - Failed to disable MCN", true);
		}
		else
		{
			SAFE_LOG(out, "DisableMCN - Failed to enable MCN", true);
		}
	}
	else
	{
		//Successfull result
		if (state)
		{
			SAFE_LOG(out, "DisableMCN - MCN disabled", true);
		}
		else
		{
			SAFE_LOG(out, "DisableMCN - MCN enabled", true);
		}
	}
	if (!state)
	{
		if (!CloseHandle(MCNHandle))
		{
			SAFE_LOG(out, "DisableMCN - Failed to close MCN temporary handle", true);
		}
		else
			MCNHandle = 0;
	}
	return status;
}

int CScsiCommand::LockDevice()
{
	DWORD BytesReturned;
	BOOL bSuccess = DeviceIoControl(fd, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, (LPDWORD) &BytesReturned, (LPOVERLAPPED) NULL);
	if (bSuccess != 0)
		bSuccess = DeviceIoControl(fd, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, (LPDWORD) &BytesReturned, (LPOVERLAPPED) NULL);
	return bSuccess;
}

int CScsiCommand::UnlockDevice()
{	
	return DeviceIoControl(fd, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, (LPDWORD) &BytesReturned, (LPOVERLAPPED) NULL);
}
 
int CScsiCommand::sendcommand(Direction dir, unsigned char *buf, size_t sz)
{
	DWORD error = 0;
	if (fd == INVALID_HANDLE_VALUE)
	{
		if (CurrentDevice)
			associate(CurrentDevice->m_cDriveLetter);
		else return 0;
	}
	p.spt.DataBuffer = buf;
	p.spt.DataTransferLength = (UINT)sz;
	p.spt.DataIn = dir;
	error = DeviceIoControl(fd, 
		                          IOCTL_SCSI_PASS_THROUGH_DIRECT, 
								  &p, 
								  sizeof(p.spt), 
								  &p, sizeof(p), 
								  &bytes, 
								  FALSE) == 0 || p.sense[0]&0x70;
	return error;
}

int CScsiCommand::transport(Direction dir, UCHAR *buf, size_t sz)
{ 
	DWORD error = sendcommand(dir, buf, sz);
	CloseAssociateHandle();
	return error;
}

unsigned char& CScsiCommand::operator[] (size_t i)
{	
	if (i==0)
	{   
		//memset(&p,0,sizeof(p));
		memset(&p, 0 , sizeof(SPKG));
		p.spt.Length = sizeof(p.spt);
		p.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
		p.spt.TimeOutValue = 108000;
		p.spt.SenseInfoLength = sizeof(p.sense);
		p.spt.SenseInfoOffset = offsetof(SPKG,sense);
	}
	p.spt.CdbLength = (UCHAR)i+1;
	return p.spt.Cdb[i];
}
